/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.editor;

import java.util.HashSet;
import java.util.Set;

import com.google.gwt.editor.client.CompositeEditor;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorDelegate;
import com.google.gwt.editor.client.LeafValueEditor;


/**
 * When you have and editor of class A, and you want this editor to be able to edit class B extending A (this is
 * possible by default but you can't access fields of B).<br>
 * To be able to access fields of B create and editor of this type named b and add it with a "" Path annotation in
 * editor for class A.<br>
 * You will then be able to access fields of class B from editor for class A through b.
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <SUPER_TYPE>
 *           the parent type of type TYPE that will be edited (class A in the example above)
 * @param <TYPE>
 *           extends SUPER_TYPE (class B in the example above)
 * @param <EDITOR>
 *           an editor for TYPE (editor for B)
 */
public abstract class AbstractDkExtendingTypeEditor<SUPER_TYPE, TYPE extends SUPER_TYPE, EDITOR extends Editor<TYPE>> implements
		CompositeEditor<SUPER_TYPE, TYPE, EDITOR>, LeafValueEditor<SUPER_TYPE>
{
	private EditorChain<TYPE, EDITOR> chain;
	private SUPER_TYPE currentValue;
	private final EDITOR subEditor;
	private final Set<Class<?>> typeClass;

	/**
	 * Construct an AbstractExtendingTypeEditor backed by the given sub-Editor.
	 * 
	 * @param subEditor
	 *           the sub-Editor that will be attached to the Editor hierarchy
	 * @param editedTYPEclass
	 *           the type class (corresponding to class type parameter: TYPE)
	 */
	public AbstractDkExtendingTypeEditor(final EDITOR subEditor,
			@SuppressWarnings("unchecked") final Class<? extends TYPE>... editedTYPEclass)
	{
		this.subEditor = subEditor;
		typeClass = new HashSet<Class<?>>();
		for (final Class<? extends TYPE> class1 : editedTYPEclass)
		{
			typeClass.add(class1);
		}
	}

	/**
	 * Returns the sub-Editor that the OptionalFieldEditor was constructed with.
	 * 
	 * @return an {@link Editor} of type E
	 */
	@Override
	public EDITOR createEditorForTraversal()
	{
		return subEditor;
	}

	@Override
	public void flush()
	{
		currentValue = chain.getValue(subEditor);
	}

	/**
	 * Returns an empty string because there is only ever one sub-editor used.
	 */
	@Override
	public String getPathElement(final EDITOR subEditor)
	{
		return "";
	}

	@Override
	public SUPER_TYPE getValue()
	{
		return currentValue;
	}

	@Override
	public void onPropertyChange(final String... paths)
	{
	}

	@Override
	public void setDelegate(final EditorDelegate<SUPER_TYPE> delegate)
	{
	}

	@Override
	public void setEditorChain(final EditorChain<TYPE, EDITOR> chain)
	{
		this.chain = chain;
	}

	@Override
	@SuppressWarnings("unchecked")
	public void setValue(final SUPER_TYPE value)
	{
		if (currentValue != null && value == null)
		{
			chain.detach(subEditor);
		}
		currentValue = value;
		final boolean instanceOf = value != null && typeClass.contains(value.getClass());
		if (instanceOf)
		{
			chain.attach((TYPE) value, subEditor);
		}
		invalidateUi(instanceOf);
	}

	protected void invalidateUi(final boolean instanceOf)
	{
		// Nothing done by default
	}

	/**
	 * @return the {@link Editor} associated to this sub type editor
	 */
	public EDITOR getEditor()
	{
		return subEditor;
	}

}
